#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define SIZE 10000

typedef struct msg_t {
	int				data;
	struct	msg_t	*next;
} msg_t;

typedef struct global_t {
	msg_t			*workq;
	pthread_cond_t	qready;
	pthread_mutex_t qlock;
	int				size;			/*  msg to be processed in queue */
} global_t;

void *process_msg(void *arg){
	msg_t		*mp;
	global_t	*global;
	int			done;

	global = (global_t *)arg;
	done = 0;

	while(done < SIZE) {
		pthread_mutex_lock(&global->qlock);
		
		while(global->size < 1){
			printf("Start to wait\n");
			pthread_cond_wait(&global->qready, &global->qlock);
			printf("Wait to end..\n");
		}

		mp = global->workq;
		global->workq = mp->next;
		global->size--;
		pthread_mutex_unlock(&global->qlock);
		printf("processing%d\n", mp->data);
		done++;
	}

	return (void*)0;
}

void enqueue_msg(global_t *global, msg_t *mp){
	pthread_mutex_lock(&global->qlock);
	mp->next = global->workq;
	global->workq = mp;
	global->size++;
	pthread_mutex_unlock(&global->qlock);
	pthread_cond_signal(&global->qready);
}

void *generate_msg(void *arg){
	int			i;
	global_t	*global;
	msg_t		*msg;

	global = (global_t *)arg;

	for(i=0; i<SIZE; i++) {
		msg = malloc(sizeof(msg_t));
		msg->data = i;
		printf("%d enqueuing....\n", i);
		enqueue_msg(global, msg);
		printf("%d enqueued done\n", i);
	}

	return (void *)0;
}

int main() {
	pthread_t		ptid, gtid;
	global_t		global;
	void			*tret;

	global.workq = NULL;
	global.size = 0;

	pthread_cond_init(&global.qready, NULL);
	pthread_mutex_init(&global.qlock, NULL);

	pthread_create(&ptid, NULL, process_msg, &global);
	pthread_create(&gtid, NULL, generate_msg, &global);

	pthread_join(ptid, &tret);
	pthread_join(gtid, &tret);

	return 0;
}
